#include "main.h"
#include <vector>
#include <GL/gl.h>
#include <GL/glu.h>
#include "canvas.h"
#include <math.h>

Canvas* canvas = new Canvas();

CanvasObject::CanvasObject() {}
CanvasObject::~CanvasObject() {}
void CanvasObject::Render(int pass) {}

Canvas::Canvas() {}
Canvas::~Canvas() { Clear(); }

int Canvas::Add(CanvasObject* obj)
{
	v.push_back(obj);
	return v.size()-1; // yes, this is one more than the index.
}

CanvasObject* Canvas::Get(int id)
{
	if (id < 0 || id >= v.size())
	{
		printf("Canvas::Get(): Invalid id!\n");
		return 0;
	}
	return v[id];
}

void Canvas::Clear()
{
	for (unsigned int i = 0, e = v.size(); i < e; i++)
		delete v[i];
}

void Canvas::Render(int pass)
{
	for (unsigned int i = 0, e = v.size(); i < e; i++)
		v[i]->Render(pass);
}

int tex = -1;

void canvas_draw()
{
	if (tex == -1) tex = texture_load("data/cloud.png");

	float amb[4] = {0.3,0.3,0.3,0};
	float dif[4] = {0.8,0.8,0.8,0};
	float pos[4] = {-2,4,2,1};

	pos[0] = 10 * cos(vid_now()/10);
	pos[1] = 10;
	pos[2] = 10 * sin(vid_now()/10);

	glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
	glEnable(GL_LIGHT0);

	glLightfv(GL_LIGHT0, GL_POSITION, pos);

	glPushAttrib(GL_ENABLE_BIT);
		glEnable(GL_STENCIL_TEST);

		glClear(GL_STENCIL_BUFFER_BIT);
		glStencilFunc(GL_ALWAYS, 1, 1);
		glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

		glEnable(GL_POLYGON_OFFSET_FILL);
			canvas->Render(0);
		glDisable(GL_POLYGON_OFFSET_FILL);

		glStencilFunc(GL_EQUAL, 1, 1);
		glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

		glDisable(GL_LIGHTING);
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_FOG);
		glPushMatrix();
			glLoadIdentity();
			glColor4f(0,0,0,1);
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D, tex);
			glBegin(GL_QUADS);
				glTexCoord2f(0,0);
				glVertex3f(10,10,-10);
				glTexCoord2f(1,0);
				glVertex3f(-10,10,-10);
				glTexCoord2f(1,1);
				glVertex3f(-10,-10,-10);
				glTexCoord2f(0,1);
				glVertex3f(10,-10,-10);
			glEnd();
		glPopMatrix();

	glPopAttrib();

	glDepthMask(0);
		canvas->Render(1);
		draw_infaxes();
	glDepthMask(1);
}


Box::Box(int _x1, int _y1, int _z1, int _x2, int _y2, int _z2)
{
	x1 = _x1; y1 = _y1; z1 = _z1; x2 = _x2; y2 = _y2; z2 = _z2;
	int t;
	if (x1 > x2) { t = x1; x1 = x2; x2 = t; }
	if (y1 > y2) { t = y1; y1 = y2; y2 = t; }
	if (z1 > z2) { t = z1; z1 = z2; z2 = t; }
}

Box::~Box() {}

//int texid = -1;

void Box::Render(int pass)
{
	glPushMatrix();
	glTranslatef(x1,y1,z1);
	glScalef(x2-x1,y2-y1,z2-z1);

	float vertex[][3] = {
		0,0,0, 0,0,1, 0,1,0, 0,1,1, 1,0,0, 1,0,1, 1,1,0, 1,1,1,
	};

	float normal[][3] = {
		-1,0,0,
		0,0,1,
		1,0,0,
		0,0,-1,
		0,-1,0,
		0,1,0,
	};

//	if (texid == -1)
//		texid = texture_load("data/texture1.jpg");

	switch (pass)
	{
	case 0:
	{
		glColor4f(0.6,0.6,1,1);
		int idx[] = {
			0, 1, 3, 2,
			1, 5, 7, 3,
			5, 4, 6, 7,
			4, 0, 2, 6,
			0, 4, 5, 1,
			2, 3, 7, 6, -1};

//		glEnable(GL_TEXTURE_2D);
//		glBindTexture(GL_TEXTURE_2D, texid);

		glBegin(GL_QUADS);
		for (int i = 0; idx[i] >= 0; i++)
		{	
			if (i%4 == 0)
				glNormal3fv(normal[i/4]);
			glVertex3fv(vertex[idx[i]]);
//			glTexCoord3fv(vertex[idx[i]]);
		}
		glEnd();

//		glDisable(GL_TEXTURE_2D);
	} break;
	case 1:
		glColor4f(0,0,0,0.5);
		glBegin(GL_LINE_LOOP);
			glVertex3f(1,1,1);
			glVertex3f(1,1,0);
			glVertex3f(1,0,0);
			glVertex3f(1,0,1);
		glEnd();
		glBegin(GL_LINE_LOOP);
			glVertex3f(0,0,1);
			glVertex3f(0,0,0);
			glVertex3f(0,1,0);
			glVertex3f(0,1,1);
		glEnd();
		glBegin(GL_LINES);
			glVertex3f(1,1,1); glVertex3f(0,1,1);
			glVertex3f(1,1,0); glVertex3f(0,1,0);
			glVertex3f(1,0,1); glVertex3f(0,0,1);
			glVertex3f(1,0,0); glVertex3f(0,0,0);
		glEnd();

	break;
	}

	glPopMatrix();
}

int l_Box(lua_State* L)
{
	lua_pushlightuserdata(L, (void*)canvas->Add(new Box(
		luaL_checkinteger(L,1),
		luaL_checkinteger(L,2),
		luaL_checkinteger(L,3),
		luaL_checkinteger(L,4),
		luaL_checkinteger(L,5),
		luaL_checkinteger(L,6)
		)));
	return 1;
}

Spider::Spider(float _x, float _y, float _z)
{
	x = _x; y = _y; z = _z;
	for (int i = 0; i < 6; i++)
	{
		legs[i][0] = legs[i][1] = legs[i][2] = 0;
		mode[i] = false;
	}
}

Spider::~Spider() { }

static GLUquadric* spider_quadric = 0;

void Spider::Render(int pass)
{
	switch (pass)
	{
	case 0:
		glPushMatrix();
		glTranslatef(x,y,z);
		if (!spider_quadric)
		{
			spider_quadric = gluNewQuadric();
			gluQuadricDrawStyle(spider_quadric, GLU_FILL);
		}
		glScalef(1,0.75,1);
		glPushAttrib(GL_ENABLE_BIT);
			glDisable(GL_DEPTH_TEST);
			glDisable(GL_LIGHTING);
			glColor4f(0.4,0,0,0.5);
			gluSphere(spider_quadric, 0.225, 20, 20);
		glPopAttrib();
		glColor4f(1.0,0.3,0.3,1);
		gluSphere(spider_quadric, 0.2, 20, 20);
		glPopMatrix();
	break;
	case 1:
		glColor4f(0.4,0,0,0.5);
		glBegin(GL_LINES);
		for (int i = 0; i < 6; i++)
		{
			if (mode[i]) {
				glVertex3f(x,y,z);
				glVertex3f(legs[i][0], legs[i][1], legs[i][2]);
			} else {
				glVertex3f(x,y,z);
				glVertex3f(x+legs[i][0], y+legs[i][1], z+legs[i][2]);
			}
		}
		glEnd();
#if 0
		glScalef(0.2,0.2,0.2);
		glScalef(1,0.75,1);
		camera_unrotate();

		glDisable(GL_DEPTH_TEST);
		glLineWidth(10);
		glBegin(GL_LINE_LOOP);
		int sides = 50;
		for (float f = 0; f < 2*M_PI - 1e-5; f+= M_PI*2 / sides)
			glVertex3f(cos(f),0,sin(f));
		glEnd();
		glLineWidth(1);
		glEnable(GL_DEPTH_TEST);
#endif
	break;
	}
}

void Spider::MoveTo(float _x, float _y, float _z)
{
	x += _x; y += _y; z += _z; 
}

void Spider::MoveLeg(int leg, float _x, float _y, float _z)
{
	legs[leg][0] = _x;
	legs[leg][1] = _y;
	legs[leg][2] = _z;
}

void Spider::SetLegMode(int leg, bool b)
{
	if (mode[leg] && !b)
	{
		// absolute -> relative
		legs[leg][0] -= x;
		legs[leg][1] -= y;
		legs[leg][2] -= z;
	}
	else if (!mode[leg] && b)
	{
		// relative -> absolute
		legs[leg][0] += x;
		legs[leg][1] += y;
		legs[leg][2] += z;
	}
	mode[leg] = b;
}

bool Spider::GetLegMode(int leg)
{
	return mode[leg];
}

void Spider::luapushpos(lua_State* L)
{
	lua_pushnumber(L, x);
	lua_pushnumber(L, y);
	lua_pushnumber(L, z);
}

void Spider::luapushlegpos(lua_State* L, int leg)
{
	if (leg < 0 || leg >= 6)
	{
		printf("Spider::MoveLeg(): invalid leg number %i!\n", leg);
		return;
	}
	lua_pushnumber(L, legs[leg][0]);
	lua_pushnumber(L, legs[leg][1]);
	lua_pushnumber(L, legs[leg][2]);
}

void Spider::luapushlegmode(lua_State* L, int leg)
{
	if (leg < 0 || leg >= 6)
	{
		printf("Spider::MoveLeg(): invalid leg number %i!\n", leg);
		return;
	}
	lua_pushboolean(L, mode[leg]);
}

int l_Spider(lua_State* L)
{
	lua_pushlightuserdata(L, (void*)canvas->Add(new Spider(
		luaL_checknumber(L,1),
		luaL_checknumber(L,2),
		luaL_checknumber(L,3)
		)));
	return 1;
}

Spider* getspider(lua_State* L, int param)
{
	luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
	int id = (int) lua_topointer(L,1);
	CanvasObject* obj = canvas->Get(id);
	Spider* s = dynamic_cast<Spider*>(obj);
	luaL_argcheck(L, (int)s, param, "not a spider id");
}

int getlegnumber(lua_State* L, int param)
{
	int leg = luaL_checkint(L,2);
	luaL_argcheck(L, (leg >= 0 && leg < 6), 2, "invalid leg number");
	return leg;
}

int l_Spider_Move(lua_State* L)
{
	getspider(L, 1)->MoveTo(
		luaL_checknumber(L,2),
		luaL_checknumber(L,3),
		luaL_checknumber(L,4)
		);
	return 0;
}

int l_Spider_MoveLeg(lua_State* L)
{
	getspider(L, 1)->MoveLeg(
		getlegnumber(L,2),
		luaL_checknumber(L,3),
		luaL_checknumber(L,4),
		luaL_checknumber(L,5));
	return 0;
}

int l_Spider_GetPos(lua_State* L)
{
	getspider(L, 1)->luapushpos(L);
	return 3;
}

int l_Spider_GetLegPos(lua_State* L)
{
	getspider(L, 1)->luapushlegpos(L,
		getlegnumber(L,2));
	return 3;
}

int l_Spider_SetLegMode(lua_State* L)
{
	getspider(L, 1)->SetLegMode(
		getlegnumber(L,2),
		luaL_checkboolean(L,3));
	return 0;
}

int l_Spider_GetLegMode(lua_State* L)
{
	lua_pushboolean(L,
		getspider(L, 1)->GetLegMode(
			getlegnumber(L,2)));
	return 1;		
}
